package lexer

import (
	"errors"
	"fmt"
	"learning/goclac/states"
	"learning/goclac/tokens"
)

type tLexerState struct {
	state states.STATES
	tokens []*tokens.Token
	buffer []rune
	i0 int
	i1 int
	d0 int
	d1 int
}

func (me *tLexerState) AppendToken(t *tokens.Token) {
	me.tokens = append(me.tokens, t)
}


func (me *tLexerState) AppendChar(it rune) {
	me.buffer = append(me.buffer, it)
}

func (me *tLexerState) BufferSize() int {
	return len(me.buffer)
}

func (me *tLexerState) IntSize() int {
	return me.i1 - me.i0 + 1
}

func (me *tLexerState) FragSize() int {
	return me.d1 - me.d0 + 1
}


func Parse(line string) ([]*tokens.Token, error) {
	var state = &(tLexerState{
		state: states.INITIAL,
		tokens: make([]*tokens.Token, 0),
		buffer: make([]rune, 0),
		i0 : 0,
		i1 : 0,
		d0: 0,
		d1: 0,
	})

	for i, it := range line + "\n" {
		e := parseChar(state, i, it)
		if e != nil {
			return nil, e
		}
	}

	return state.tokens, nil
}

func parseChar(state *tLexerState, i int, it rune) error {
	var e error = nil

	switch state.state {
	case states.INITIAL:
		e = parseCharWhenInitial(state, i, it)
		break

	case states.INT_STATUS:
		e = parseCharWhenInt(state, i, it)
		break

	case states.DOT_STATUS:
		e = parseCharWhenDot(state, i, it)
		break

	case states.FRAG_STATUS:
		e = parseCharWhenFrag(state, i, it)
		break
	}

	return e
}

func parseCharWhenInitial(state *tLexerState, i int, it rune) error {
	if is_minus(it) || is_0_to_9(it) {
		state.state = states.INT_STATUS
		state.buffer = make([]rune, 0)
		state.buffer = append(state.buffer, it)
		state.i0 = i
		state.i1 = i

	} else if is_space(it){
		return nil

	} else if is_add(it) {
		state.AppendToken(tokens.OfRune(tokens.ADD, it, i))
	} else if is_sub(it) {
		state.AppendToken(tokens.OfRune(tokens.SUB, it, i))
	} else if is_mul(it) {
		state.AppendToken(tokens.OfRune(tokens.MUL, it, i))
	} else if is_div(it) {
		state.AppendToken(tokens.OfRune(tokens.DIV, it, i))
	} else if is_lb(it) {
		state.AppendToken(tokens.OfRune(tokens.LB, it, i))
	}  else if is_rb(it) {
		state.AppendToken(tokens.OfRune(tokens.RB, it, i))
	} else {
		return errors.New(fmt.Sprintf("parseCharWhenInitial, invalid char %c at %d", it, i))
	}

	return nil
}


func parseCharWhenInt(state *tLexerState, i int, it rune) error {
	if is_0_to_9(it) {
		if state.BufferSize() >= 10 {
			return errors.New(fmt.Sprintf("too large int number at %v", i))
		} else {
			state.AppendChar(it)
			state.i1 = i
		}
	} else if is_dot(it) {
		state.AppendChar(it)
		state.state = states.DOT_STATUS

	} else if is_space(it) {
		state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1))
		state.state = states.INITIAL

	} else if is_rb(it) {
		state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.RB, it, i))
		state.state = states.INITIAL

	}  else if is_add(it) {
		state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.ADD, it, i))
		state.state = states.INITIAL

	} else if is_sub(it) {
		state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.SUB, it, i))
		state.state = states.INITIAL

	} else if is_mul(it) {
		state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.MUL, it, i))
		state.state = states.INITIAL

	} else if is_div(it) {
		state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.DIV, it, i))
		state.state = states.INITIAL

	} else {
		return errors.New(fmt.Sprintf("parseCharWhenInt, invalid char %c at %d", it, i))
	}

	return nil
}


func parseCharWhenDot(state *tLexerState, i int, it rune) error {
	if is_0_to_9(it) {
		state.state = states.FRAG_STATUS
		state.AppendChar(it)
		state.d0 = i
		state.d1 = i
	} else {
		return errors.New(fmt.Sprintf("parseCharWhenDot, invalid char %c at %d", it, i))
	}

	return nil
}


func parseCharWhenFrag(state *tLexerState, i int, it rune) error {
	if is_0_to_9(it) {
		if state.FragSize() >= 10 {
			return errors.New(fmt.Sprintf("too many chars for a double value at %d", i))
		} else {
			state.AppendChar(it)
			state.d1 = i
		}
	} else if is_space(it) {
		state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1))
		state.state = states.INITIAL

	} else if is_add(it) {
		state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.ADD, it, i))
		state.state = states.INITIAL

	} else if is_sub(it) {
		state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.SUB, it, i))
		state.state = states.INITIAL

	} else if is_mul(it) {
		state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.MUL, it, i))
		state.state = states.INITIAL

	} else if is_div(it) {
		state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.DIV, it, i))
		state.state = states.INITIAL

	} else if is_rb(it) {
		state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1))
		state.AppendToken(tokens.OfRune(tokens.RB, it, i))
		state.state = states.INITIAL

	} else {
		return errors.New(fmt.Sprintf("parseCharWhenFrag, invalid char %c at %d", it, i))
	}

	return nil
}